package yach

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/uniplaces/carbon"
	"io/ioutil"
	"net/http"
	"net/url"
)

type RobotConfig struct {
	Secret string `toml:"secret"`
	Url    string `toml:"url"`
}

func GetSign(secret string) (string, int64) {
	timestamp := carbon.Now().UnixNano() / 1e6
	data := fmt.Sprintf("%d\n%s", timestamp, secret)
	h := hmac.New(sha256.New, []byte(secret))
	h.Write([]byte(data))
	sum := h.Sum(nil)
	base64b := base64.StdEncoding.EncodeToString(sum)
	return url.QueryEscape(base64b), timestamp
}

type At struct {
	AtMobiles []string `json:"atMobiles"`
	IsAtAll   bool     `json:"isAtAll"`
}
type Text struct {
	Content string `json:"content"`
}
type Markdown struct {
	Title string `json:"title"`
	Image string `json:"image"`
	Text  string `json:"text"`
}
type Link struct {
	MessageUrl string `json:"message_url"`
	PicUrl     string `json:"pic_url"`
	Title      string `json:"title"`
	Text       string `json:"text"`
}

type Image struct {
	Url string `json:"url"`
}

//type ActionCard struct {
//	Title        string `json:"title"`
//	Markdown     string `json:"markdown"`
//	Image        string `json:"image"`
//	ContentTitle string `json:"content_title"`
//	SingleTitle  string `json:"single_title"`
//	SingleUrl    string `json:"single_url"`
//	BtnType      int    `json:"btn_type"`
//}
type MsgType string

const (
	MsgTypeText     MsgType = "text"
	MsgTypeMarkdown         = "markdown"
	MsgTypeLink             = "link"
	MsgTypeImg              = "image"
)

// 文本消息
type textMsg struct {
	MsgType MsgType `json:"msgtype"`
	Text    Text    `json:"text"`
	At      At      `json:"at"`
}

// Markdown 消息
type markdownMsg struct {
	MsgType  MsgType  `json:"msgtype"`
	Markdown Markdown `json:"markdown"`
	At       At       `json:"at"`
}

// 链接消息
type linkMsg struct {
	MsgType MsgType `json:"msgtype"`
	Link    Link    `json:"link"`
}

// 图片消息
type imgMsg struct {
	MsgType MsgType `json:"msgtype"`
	Image   Image   `json:"image"`
}

func PushMsg(cfg RobotConfig, msg interface{}) (string, error) {
	var at At
	msg = buildMsg(msg, at)
	sign, ts := GetSign(cfg.Secret)
	sub := fmt.Sprintf("&timestamp=%d&sign=%s", ts, sign)
	yurl := fmt.Sprintf("%s%s", cfg.Url, sub)
	msgb, err := json.Marshal(msg)
	if err != nil {
		return "", err
	}
	b := bytes.NewBuffer(msgb)
	resp, err := http.Post(yurl, "application/json", b)
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	return string(body), err
}

func buildMsg(msg interface{}, at At) interface{} {
	var rzt interface{}
	switch msg := msg.(type) {
	case Text:
		rzt = textMsg{
			MsgType: MsgTypeText,
			Text:    msg,
			At:      at,
		}
	case Markdown:
		rzt = markdownMsg{
			MsgType:  MsgTypeMarkdown,
			Markdown: msg,
			At:       at,
		}
		break
	case Link:
		rzt = linkMsg{
			MsgType: MsgTypeLink,
			Link:    msg,
		}
		break
	case Image:
		rzt = imgMsg{
			MsgType: MsgTypeImg,
			Image:   msg,
		}
	}
	return rzt
}
